#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include "./locker/locker.h"
#include "./pool/threadpool.h"
#include <signal.h>
#include "./http_conn/http_conn.h"
#include "./timer/lst_timer.h"
#include "./log/log.h"
#include "./mysql_conn/sql_connection_pool.h"

#define MAX_FD 65535
#define MAX_EVENT_NUMBER 10000
#define TIMESLOT 3

#define CHECK_CLOSE_TIMER m_close_timer == 0

static int pipefd[2];
static sort_timer_lst timer_lst;
static int epollfd = 1;

// 日志0开启 1关闭
int m_close_log = 1;
// 定时器0开启 1关闭
int m_close_timer = 1;

// 添加信号捕捉
void addsig(int sig, void(handler)(int))
{
    struct sigaction sa;
    memset(&sa, '\0', sizeof(sa));
    sa.sa_handler = handler;
    sigfillset(&sa.sa_mask);
    sigaction(sig, &sa, NULL);
}

extern void addfd(int epollfd, int fd, bool oneshot);

extern void removefd(int epollfd, int fd);

extern void modfd(int epollfd, int fd, int ev);

extern void setnonblocking(int fd);

void sig_handler(int sig)
{
    int save_errno = errno;
    int msg = sig;
    send(pipefd[1], (char *)&msg, 1, 0);
    errno = save_errno;
}

void addsig(int sig)
{
    struct sigaction sa;
    memset(&sa, '\0', sizeof(sa));
    sa.sa_handler = sig_handler;
    sa.sa_flags |= SA_RESTART;
    sigfillset(&sa.sa_mask);
    assert(sigaction(sig, &sa, NULL) != -1);
}

void timer_handler()
{
    // 定时处理任务，实际上就是调用tick()函数
    timer_lst.tick();
    // 因为一次 alarm 调用只会引起一次SIGALARM 信号，所以我们要重新定时，以不断触发 SIGALARM信号。
    alarm(TIMESLOT);
}

void call_back(client_data *user_data)
{
    LOG_INFO("user_data->sockfd=%d\n", user_data->sockfd);
    epoll_ctl(epollfd, EPOLL_CTL_DEL, user_data->sockfd, 0);
    // assert(user_data);
    close(user_data->sockfd);
    http_conn::m_user_count--;
}

int main(int argc, char *argv[])
{
    const char *docker = std::getenv("DOCKER");
    if (docker != nullptr)
    {
        cout << "webserver exec..." << endl;
    }
    // 最后一个参数>0为异步 =0为同步
    Log::get_instance()->init("./ServerLog", 0, 2000, 800000, 0);
    if (argc <= 1)
    {
        LOG_ERROR("exe:%s port\n", basename(argv[0]));
        exit(-1);
    }

    // 获取端口号
    int port = atoi(argv[1]);

    // 处理信号
    addsig(SIGPIPE, SIG_IGN);

    // 客户端信息
    http_conn *users = new http_conn[MAX_FD];

    // 初始化
    connection_pool *m_connPool = connection_pool::GetInstance();
    const char *host = std::getenv("DB_HOST");
    if (host == nullptr)
    {
        host = "127.0.0.1";
    }
    m_connPool->init(host, "root", "zstu20Quchenghao!", "Webserver_db", 3306, 10, m_close_log);
    //}
    users->initmysql_result(m_connPool);
    // 创建线程池
    threadpool<http_conn> *pool = NULL;
    try
    {
        pool = new threadpool<http_conn>(m_connPool);
    }
    catch (...)
    {
        LOG_ERROR("create threadpool failed\n");
        exit(-1);
    }
    // 创建监听的套接字
    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    if (listenfd < 0)
    {
        LOG_ERROR("socket() failed\n");
        exit(-1);
    }
    // assert(listenfd >= 0);
    // 端口复用
    int reuse = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    // 绑定
    int ret = 0;
    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(port);
    ret = bind(listenfd, (struct sockaddr *)&address, sizeof(address));
    // assert(ret != -1);
    if (ret == -1)
    {
        LOG_ERROR("bind() failed\n");
        exit(-1);
    }

    // 监听
    ret = listen(listenfd, 5);
    if (ret == -1)
    {
        LOG_ERROR("listen() failed\n");
        exit(-1);
    }
    // assert(ret != -1);

    // 创建epoll
    epoll_event events[MAX_EVENT_NUMBER];
    int epollfd = epoll_create(5);
    if (epollfd == -1)
    {
        LOG_ERROR("epoll_create() failed\n");
        exit(-1);
    }
    // assert(epollfd != -1);

    // 添加到epoll
    addfd(epollfd, listenfd, false);
    http_conn::m_epollfd = epollfd;
    // 创建管道
    ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
    // assert(ret != -1);
    if (epollfd == -1)
    {
        LOG_ERROR("epoll_create() failed\n");
        exit(-1);
    }
    setnonblocking(pipefd[1]);
    addfd(epollfd, pipefd[0], false);

    // 设置信号处理函数
    addsig(SIGALRM);
    addsig(SIGTERM);
    bool stop_server = false;

    client_data *clientdata = new client_data[MAX_FD];
    bool timeout = false;
    alarm(TIMESLOT); // 定时,5秒后产生SIGALARM信号->为了测试方便设置为1s
    while (!stop_server)
    {
        int num_events = epoll_wait(epollfd, events, MAX_FD, -1);
        if (num_events == -1)
        {
            if (errno == EINTR) // epoll_wait会被SIGALRM信号中断返回-1
            {
                continue;
            }
            LOG_ERROR("epoll_wait return -1 and errno == EINTR");
            exit(-1);
        }
        for (int i = 0; i < num_events; i++)
        {
            int sockfd = events[i].data.fd;
            if (events[i].data.fd == listenfd)
            {
                struct sockaddr_in conaddr;
                socklen_t conlen = sizeof(conaddr);
                int confd = accept(listenfd, (struct sockaddr *)&conaddr, &conlen);
                if (confd == -1)
                {
                    continue;
                }
                if (http_conn::m_user_count > MAX_FD)
                {
                    close(confd);
                    continue;
                }
                else
                {
                    users[confd].init(confd, conaddr);
                    clientdata[confd].sockfd = confd;
                    clientdata[confd].address = conaddr;
                    if (CHECK_CLOSE_TIMER)
                    {
                        // 创建定时器，设置其回调函数与超时时间，然后绑定定时器与用户数据，最后将定时器添加到链表timer_lst中
                        util_timer *timer = new util_timer;
                        timer->call_back = call_back;
                        time_t cur = time(NULL);
                        timer->expire = cur + 3 * TIMESLOT;
                        clientdata[confd].timer = timer;
                        timer->user_data = &clientdata[confd];
                        timer_lst.add_timer(timer);
                    }
                }
            }
            else if ((sockfd == pipefd[0]) && (events[i].events & EPOLLIN))
            {
                // 处理信号
                int sig;
                char signals[1024];
                int ret = recv(pipefd[0], signals, sizeof(signals), 0);
                if (ret == -1)
                {
                    continue;
                }
                else if (ret == 0)
                {
                    continue;
                }
                else
                {
                    for (int i = 0; i < ret; ++i)
                    {
                        switch (signals[i])
                        {
                        case SIGALRM:
                        {
                            // 用timeout变量标记有定时任务需要处理，但不立即处理定时任务
                            // 这是因为定时任务的优先级不是很高，我们优先处理其他更重要的任务
                            timeout = true;
                            break;
                        }
                        case SIGTERM:
                        {
                            stop_server = true;
                        }
                        }
                    }
                }
            }
            else if (events[i].events & (EPOLLHUP | EPOLLRDHUP | EPOLLERR))
            {
                users[sockfd].close_conn();
            }
            else if (events[i].events & EPOLLIN)
            {
                if (CHECK_CLOSE_TIMER)
                {
                    util_timer *timer = clientdata[sockfd].timer;
                    time_t cur = time(NULL);
                    timer->expire = cur + 3 * TIMESLOT;
                    timer_lst.adjust_timer(timer);
                }

                if (users[sockfd].read())
                {
                    pool->append(users + sockfd);
                }
                else
                {
                    if (CHECK_CLOSE_TIMER)
                    {
                        util_timer *timer = clientdata[sockfd].timer;
                        timer_lst.del_timer(timer);
                    }
                    users[sockfd].close_conn();
                }
            }
            else if (events[i].events & EPOLLOUT)
            {
                // 一次性写完所有数据
                bool ret = users[sockfd].write();
                if (!ret)
                {
                    users[sockfd].close_conn();
                }
            }
        }
        // 先处理io再处理定时->定时功能可能不精确
        if (timeout&&CHECK_CLOSE_TIMER)
        {
            timer_handler();
            timeout = false;
        }
    }
    close(epollfd);
    close(listenfd);
    delete[] users;
    delete pool;
    return 0;
}
